#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <linux/netlink.h>
#include <linux/netfilter/nfnetlink.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>

#include "rtnetlink.h"
#include "log.h"

/**
 * set_str_attr(): Prepare a 8bytes of string netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @name: Buffer to copy into the attribute
 */
struct nlattr *set_str8_attr(struct nlattr *attr, uint16_t type, char *name)
{
    int len = 8 + sizeof(struct nlattr);
    attr->nla_type = type;
    attr->nla_len = 8;
    memcpy(NLA_ATTR(attr), name, len);

    return (void *)attr + NLA_ALIGN(len) + sizeof(struct nlattr);
}

struct nlattr *set_str_attr(struct nlattr *attr, uint16_t type, char *name)
{
    int len = strlen(name) + sizeof(struct nlattr);
    attr->nla_type = type;
    attr->nla_len = len;
    memcpy(NLA_ATTR(attr), name, len);

    return (void *)attr + NLA_ALIGN(len);
}

struct nlattr *set_strn_attr(struct nlattr *attr, uint16_t type, char *name, int len)
{
    attr->nla_type = type;
    attr->nla_len = len;
    memcpy(NLA_ATTR(attr), name, len);

    return (void *)attr + NLA_ALIGN(len);
}

/**
 * set_binary_attr(): Prepare a byte array netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @buffer: Buffer with data to send
 * @buffer_size: Size of the previous buffer
 */
struct nlattr *set_binary_attr(struct nlattr *attr, uint16_t type, uint8_t *buffer, uint64_t buffer_size)
{
    attr->nla_type = type;
    attr->nla_len = NLA_BIN_SIZE(buffer_size);
    memcpy(NLA_ATTR(attr), buffer, buffer_size);

    return (void *)attr + NLA_ALIGN(NLA_BIN_SIZE(buffer_size));
}

/**
 * set_nested_attr(): Prepare a nested netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the nested attribute
 * @data_len: Length of the nested attribute
 */
struct nlattr *set_nested_attr(struct nlattr *attr, uint16_t type, uint16_t data_len)
{
    attr->nla_type = type;
    attr->nla_len = (data_len + sizeof(struct nlattr));
    return (void *)attr + sizeof(struct nlattr);
}

/**
 * set_u32_attr(): Prepare an integer netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @value: Value of this attribute
 */
struct nlattr *set_u32_attr(struct nlattr *attr, uint16_t type, uint32_t value)
{
    attr->nla_type = type;
    attr->nla_len = sizeof(uint32_t) + sizeof(struct nlattr);
    *(uint32_t *)NLA_ATTR(attr) = htonl(value);

    return (void *)attr + sizeof(uint32_t) + sizeof(struct nlattr);
}
struct nlattr *set_u32_attr_nat(struct nlattr *attr, uint16_t type, uint32_t value)
{
    attr->nla_type = type;
    attr->nla_len = sizeof(uint32_t) + sizeof(struct nlattr);
    *(uint32_t *)NLA_ATTR(attr) = value;

    return (void *)attr + sizeof(uint32_t) + sizeof(struct nlattr);
}

/**
 * set_u16_attr(): Prepare an integer netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @value: Value of this attribute
 */
struct nlattr *set_u16_attr(struct nlattr *attr, uint16_t type, uint16_t value)
{
    attr->nla_type = type;
    attr->nla_len = NLA_ALIGN(sizeof(uint16_t) + sizeof(struct nlattr));
    *(uint16_t *)NLA_ATTR(attr) = (value);

    return (void *)attr + NLA_ALIGN(sizeof(uint16_t) + sizeof(struct nlattr));
}

/**
 * set_u8_attr(): Prepare an integer netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @value: Value of this attribute
 */
struct nlattr *set_u8_attr(struct nlattr *attr, uint16_t type, uint8_t value)
{
    attr->nla_type = type;
    attr->nla_len = NLA_ALIGN(sizeof(uint8_t) + sizeof(struct nlattr));
    *(uint8_t *)NLA_ATTR(attr) = value;

    return (void *)attr + NLA_ALIGN(sizeof(uint8_t) + sizeof(struct nlattr));
}

/**
 * set_u64_attr(): Prepare a 64 bits integer netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @value: Value of this attribute
 */
struct nlattr *set_u64_attr(struct nlattr *attr, uint16_t type, uint64_t value)
{
    attr->nla_type = type;
    attr->nla_len = sizeof(uint64_t) + sizeof(struct nlattr);
    *(uint64_t *)NLA_ATTR(attr) = htobe64(value);

    return (void *)attr + sizeof(uint64_t) + sizeof(struct nlattr);
}

/**
 * set_flag_attr(): Prepare a flag doesn't need data  integer netlink attribute
 * @attr: Attribute to fill
 * @type: Type of the attribute
 * @value: Value of this attribute
 */
struct nlattr *set_flag_attr(struct nlattr *attr, uint16_t type)
{
    attr->nla_type = type;
    attr->nla_len = sizeof(struct nlattr);
    // *(uint64_t *)NLA_ATTR(attr) = htobe64(value);

    return (void *)attr + sizeof(struct nlattr);
}
